package com.fight.strive.sys.modules.camunda.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fight.strive.sys.modules.camunda.dao.WorkflowDefinitionMapper;
import com.fight.strive.sys.modules.camunda.dto.WorkflowDefinitionDto;
import com.fight.strive.sys.modules.camunda.dto.WorkflowQueryDto;
import com.fight.strive.sys.modules.camunda.entity.WorkflowDefinitionEntity;
import com.fight.strive.sys.modules.camunda.service.WorkflowDefinitionService;
import com.fight.strive.sys.modules.exception.StriveException;
import com.fight.strive.sys.modules.request.dto.PageRequest;
import com.fight.strive.sys.modules.response.dto.PageData;
import com.fight.strive.sys.modules.sysadmin.utils.SysAdminUtils;
import com.fight.strive.sys.utils.IOUtils;
import com.fight.strive.sys.utils.ResponseUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZHOUXIANG
 */
@Service
@Slf4j
public class WorkflowDefinitionServiceImpl
        extends ServiceImpl<WorkflowDefinitionMapper, WorkflowDefinitionEntity>
        implements WorkflowDefinitionService {

    @Resource
    private RepositoryService repositoryService;

    @Override
    public PageData<WorkflowDefinitionDto> listDefinitionByPage(
            PageRequest<WorkflowDefinitionDto> pageRequest) {
        WorkflowDefinitionDto condition = pageRequest.getCondition();
        Long tenantId = SysAdminUtils.getCurrentTenantId();
        ProcessDefinitionQuery query =
                repositoryService.createProcessDefinitionQuery()
                        .tenantIdIn(String.valueOf(tenantId));
        // 如果versionTag为latest则查最新的流程定义
        if (StringUtils.equalsIgnoreCase(
                condition.getVersionTag(), "latest")) {
            query.latestVersion();
        }
        // 根据流程名称查询
        if (StringUtils.isNotBlank(condition.getName())) {
            query.processDefinitionNameLike(
                    "%".concat(condition.getName()).concat("%"));
        }
        List<ProcessDefinition> list = query
                .listPage((int) pageRequest.getStart(0),
                        (int) pageRequest.getRows());
        long total = query.count();
        List<WorkflowDefinitionDto> definitionDtos = new ArrayList<>();
        list.forEach(def -> {
            WorkflowDefinitionDto dto = new WorkflowDefinitionDto();
            dto.setId(def.getId()).setKey(def.getKey())
                    .setName(def.getName())
                    .setVersion(def.getVersion())
                    .setVersionTag(def.getVersionTag())
                    .setCategory(def.getCategory())
                    .setDeploymentId(def.getDeploymentId())
                    .setResourceName(def.getResourceName())
                    .setDiagramResourceName(def.getDiagramResourceName())
                    .setTenantId(def.getTenantId());
            definitionDtos.add(dto);
        });
        PageData<WorkflowDefinitionDto> pageData = new PageData<>(pageRequest);
        pageData.setTotal(total).calcPages().setContent(definitionDtos);
        return pageData;
    }

    @Override
    public PageData<WorkflowDefinitionEntity> listProcessByPage(
            PageRequest<WorkflowQueryDto> pageRequest) {
        WorkflowQueryDto condition = pageRequest.getCondition();
        Long tenantId = SysAdminUtils.getCurrentTenantId();
        QueryWrapper<WorkflowDefinitionEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t1.TENANT_ID_", String.valueOf(tenantId));
        // 根据流程名称模糊查询
        queryWrapper.like(StringUtils.isNotBlank(condition.getProcessDefinitionName()),
                "t1.NAME_", condition.getProcessDefinitionName());
        // 根据候选组查询
        if (StringUtils.isNotBlank(condition.getCandidateGroups())) {
            String instr = StringUtils.getSqlInStringByArray(
                    condition.getCandidateGroups());
            queryWrapper.exists(String.format("select ID_ from act_ru_identitylink t3 " +
                    "where t3.PROC_DEF_ID_ = t1.ID_ and t3.GROUP_ID_ in (%s) ", instr));
        }
        // 根据候选用户查询
        if (StringUtils.isNotBlank(condition.getCandidateUsers())) {
            String instr = StringUtils.getSqlInStringByArray(
                    condition.getCandidateUsers());
            queryWrapper.exists(String.format("select ID_ from act_ru_identitylink t3 " +
                    "where t3.PROC_DEF_ID_ = t1.ID_ and t3.USER_ID_ in (%s) ", instr));
        }
        IPage<WorkflowDefinitionEntity> page = pageRequest.buildMybatisPlusPage();
        IPage<WorkflowDefinitionEntity> iPage = this.baseMapper.listByPage(page, queryWrapper);
        return new PageData<>(iPage);
    }

    @Override
    public void deleteProcessDefinition(WorkflowDefinitionDto dto) {
        repositoryService.deleteProcessDefinition(dto.getId(), true);
    }

    @Override
    public void downloadResourceFile(WorkflowDefinitionDto dto) {
        try {
            // 获取流程模型流，bpmn.xml内容
            InputStream inputStream = repositoryService.getProcessModel(dto.getId());
            // 输出到响应
            ResponseUtils.writeStream(ResponseUtils.getResponse(),
                    dto.getResourceName(), inputStream);
            inputStream.close();
        } catch (Exception e) {
            throw new StriveException("下载资源失败！");
        }
    }

    @Override
    public String getDefinitionXmlString(String id) {
        try {
            InputStream inputStream = repositoryService.getProcessModel(id);
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, "utf-8");
            String xml = writer.toString();
            writer.close();
            inputStream.close();
            return xml;
        } catch (Exception e) {
            throw new StriveException("获取bpmn xml字符串失败");
        }
    }
}
